package com.szwistar.common.datastruct;

import com.alibaba.fastjson.JSON;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

public class Reflector {
	static final Logger LOG = LoggerFactory.getLogger(Reflector.class);

	/**
	 * 匹配参数
	 * @param params 实际参数
	 * @param paramsRequired 需要的参数类型
	 * @return 是否匹配
	 */
	public static boolean paramsMatch(Object[] params, Class<?>[] paramsRequired) {
		boolean hasParams = Utils.isNotEmpty(params);
		boolean hasRequired = Utils.isNotEmpty(paramsRequired);

		if(hasRequired) {
			// 需要参数而没有提供参数: false
			if(!hasParams) { return false; }
			// 参数数量不匹配：false
			if(params.length != paramsRequired.length) { return false; }
			// 逐个参数类型检查
			for(int i=0; i<params.length; i++) {
				// 参数类型不匹配：false
				if(!Utils.isAssignableFrom(paramsRequired[i], params[i].getClass())) { return false; }
			}
		} else {
			// 不需要参数而提供了参数：false
			if(hasParams) { return false; }
		}
		// 其它情形：true
		return true;
	}

	public static boolean paramsMatch(List<Object> params, Class<?>[] paramsRequired) {
		Object[] arr = (params == null) ? null : params.toArray();
		return paramsMatch(arr, paramsRequired);
	}

	// 构造参数类型列表
	public static Class<?>[] getParameterTypes(Object[] params) {
		if((params == null) || (params.length == 0)) { return null; }
		Class<?>[] paramTypes = new Class<?>[params.length];

		int count = 0;
		for(Object param: params) {
			paramTypes[count++] = param.getClass();
		}
		return paramTypes;
	}

	/**
	 * 根据method的参数要求，将 JSON 格式参数列表转化为需要的调用参数列表
	 * @param paramsRequired 需要的参数类型
	 * @param paramsJson 参数 JSON 字符串
	 * @return <ul>
	 *   <li>如果不需要参数(paramsRequired 为 null/空数组)，而 JSON 也为空(null/空字符串/"[]")，则返回 Object[0]，否则异常；
	 *   <li>如果需要参数，则按参数类型对 JSON 进行解析，如果数量或类型不匹配，则异常。
	 *   </ul>
	 */
	public static Object[] parseJsonParameters(Class<?>[] paramsRequired, String paramsJson) {
		if(Utils.isEmpty(paramsRequired)) {
			// 如果不需要参数，则看 JSON 是否也为空
			if(Utils.isNotEmpty(paramsJson)) {
				List<Object> params = JSON.parseArray(paramsJson);
				if(Utils.isNotEmpty(params)) {
					throw new IllegalArgumentException("参数数量不匹配：不需要参数，而提供的参数数量为 " + params.size());
				}
			}
			return new Object[0];
		} else {
			// 如果有参数类型表，则按参数类型表来解析
			List<Object> params = JSON.parseArray(paramsJson, paramsRequired);
			if(!paramsMatch(params, paramsRequired)) {
				throw new IllegalArgumentException("参数数量或类型不匹配！");
			}
			return params.toArray();
		}
	}

	/**
	 * 根据method的参数要求，将命令行格式参数列表转化为需要的调用参数列表
	 * @param paramsRequired 需要的参数类型
	 * @param args 参数字符串
	 * @return <ul>
	 *   <li>如果不需要参数(paramsRequired 为 null/空数组)，而 args 也为空(null/空字符串/"[]")，则返回 Object[0]，否则异常；
	 *   <li>如果需要参数，则按参数类型对 args 进行解析，如果数量或类型不匹配，则异常。
	 *   </ul>
	 */
	public static Object[] parseParameters(Class<?>[] paramsRequired, String args) {
		if(Utils.isEmpty(paramsRequired)) {
			// 如果不需要参数，则看 args 是否也为空
			if(Utils.isNotEmpty(args)) {
				throw new IllegalArgumentException("参数数量不匹配：不需要参数，而提供的参数数量为 " + args.length());
			}
			return new Object[0];
		} else {
			// 如果有参数类型表，则按参数类型表来解析
			List<Object> params = JSON.parseArray(args, paramsRequired);
			if(!paramsMatch(params, paramsRequired)) {
				throw new IllegalArgumentException("参数数量或类型不匹配！");
			}
			return params.toArray();
		}
	}

	/**
	 * 根据method的参数要求，将字符串数组格式参数列表转化为需要的调用参数列表
	 * @param paramTypes 需要的参数类型。最后一个参数如是一个数组，则表示是变长参数。
	 * @param args 参数字符串数组
	 * @return <ul>
	 *   <li>如果不需要参数(paramsRequired 为 null/空数组)，而 args 也为空(null/空字符串/"[]")，则返回 Object[0]，否则异常；
	 *   <li>如果需要参数，则按参数类型对 args 进行解析，如果数量或类型不匹配，则异常。
	 *   </ul>
	 */
	public static Object[] parseParameters(Class<?>[] paramTypes, String[] args) throws Exception {
		if(Utils.isEmpty(paramTypes)) {
			// 不需要参数，也无参数提供，返回空数组
			if(Utils.isEmpty(args)) { return new Object[0]; }
			// 不需要参数，但有参数提供，报错
			throw new IllegalArgumentException("SHOULD be called with NO argument !");
		}

		// 需要的普通参数个数
		int plainCount;
		// 变长参数个数
		int vlaCount;

		// 看所需的最后一个参数是否是变长参数（就是一个数组）
		Class<?> lastType = paramTypes[paramTypes.length - 1];
		if(lastType.isArray()) {
			// 最后一个参数是变长参数
			// 计算需要的普通参数个数
			plainCount = paramTypes.length - 1;
			if(args.length < plainCount) {
				// 提供的参数过少（应不少于普通参数个数）
				throw new IllegalArgumentException(String.format("Argument provided(%d) < required(%d + *) !",
						args.length, plainCount));
			}
			// 计算变长参数数量
			vlaCount = args.length - plainCount;
		} else {
			// 无变长参数
			// 参数数量必须相等
			if(paramTypes.length != args.length) {
				throw new IllegalArgumentException(String.format("Argument provided(%d) != required(%d) !",
						args.length, paramTypes.length));
			}
			// 全部是普通参数
			plainCount = paramTypes.length;
			// 变长参数数量为 0
			vlaCount = 0;
		}

		// 创建解析输出的参数表
		Object[] params = new Object[plainCount + ((vlaCount < 1) ? 0 : 1)];

		// 解析普通参数
		for(int i = 0; i < plainCount; i++) {
			params[i] = Utils.parseValue(paramTypes[i], args[i]);
		}

		// 如果无变长参数，则直接返回解析结果
		if(vlaCount < 1) return params;

		// 解析变长参数
		// 取得变长参数类型
		Class<?> vlaType = lastType.getComponentType();
		// 创建一个变长参数数组
		Object vla = Array.newInstance(vlaType, vlaCount);
		// 解析变长参数
		for(int i = 0; i < vlaCount; i++) {
			Object va = Utils.parseValue(vlaType, args[plainCount + i]);
			Array.set(vla, i, va);
		}
		// 变长参数数组放在参数表的最后
		params[plainCount] = vla;

		return params;
	}

	// 根据参数个数进行调用
	public static Object invokeWithMultiParameters(Object pThis, Method method, Object[] params) throws Exception {
		// 参数个数
		int paramCount = (params==null) ? 0 : params.length;
		Object result;

		// 根据参数个数进行调用
		switch(paramCount) {
		case  0: result = method.invoke(pThis); break;
		case  1: result = method.invoke(pThis, params[0]); break;
		case  2: result = method.invoke(pThis, params[0], params[1]); break;
		case  3: result = method.invoke(pThis, params[0], params[1], params[2]); break;
		case  4: result = method.invoke(pThis, params[0], params[1], params[2], params[3]); break;
		case  5: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4]); break;
		case  6: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4], params[5]); break;
		case  7: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4], params[5], params[6]); break;
		case  8: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]); break;
		case  9: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]); break;
		case 10: result = method.invoke(pThis, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9]); break;
		default: throw new IllegalArgumentException("RPC 参数数量不能超过10个！");
		}

		return result;
	}

	/**
	 * 调用
	 * @param clazz 目标类
	 * @param pThis 目标对象（如果是静态函数，应为空）
	 * @param methodName 方法名
	 * @param params 参数列表
	 * @return 调用返回值
	 */
	protected static Object call(Class<?> clazz, Object pThis, String methodName, Object ...params) throws Exception {
		// 查找对应的方法
		Method method = getMethod(clazz, methodName, getParameterTypes(params));

		// 调用方法
		LOG.info("Reflector调用: {}({}).{}({})", pThis, clazz.getSimpleName(), methodName, params);
		Object ret = invokeWithMultiParameters(pThis, method, params);
		LOG.debug("Reflector调用返回：{}", ret);

		return ret;
	}

	/**
	 * 调用静态方法
	 * @param clazz 类
	 * @param methodName 方法名
	 * @param params 变长参数
	 * @return 调用返回值
	 */
	public static Object call(Class<?> clazz, String methodName, Object ...params) throws Exception {
		return call(clazz, null, methodName, params);
	}

	/**
	 * 调用对象方法
	 * @param pThis 对象
	 * @param methodName 方法名
	 * @param params 变长参数
	 * @return 调用返回值
	 */
	public static Object call(Object pThis, String methodName, Object ...params) throws Exception {
		return call(pThis.getClass(), pThis, methodName, params);
	}

	/**
	 * 使用JSON格式参数数组调用对象方法
	 * @param pThis 对象
	 * @param methodName 方法名
	 * @param paramsJson JSON格式的变长参数数组
	 * @return 调用返回值
	 */
	public static Object call(Object pThis, String methodName, String paramsJson) throws Exception {
		Method method = getMethod(pThis.getClass(), methodName);
		if(method == null) { throw new NoSuchMethodException(pThis.getClass().getName() + " 中没有方法：" + methodName); }
		Object[] params = parseJsonParameters(method.getParameterTypes(), paramsJson);
		return invokeWithMultiParameters(pThis, method, params);
	}

	/**
	 * 使用JSON格式参数数组调用类的静态方法
	 * @param clazz 类
	 * @param methodName 方法名
	 * @param paramsJson JSON格式的变长参数数组
	 * @return 调用返回值
	 */
	public static Object call(Class<?> clazz, String methodName, String paramsJson) throws Exception {
		Method method = getMethod(clazz, methodName);
		if(method == null) { throw new NoSuchMethodException(clazz.getName() + " 中没有方法：" + methodName); }
		Object[] params = parseJsonParameters(method.getParameterTypes(), paramsJson);
		return invokeWithMultiParameters(null, method, params);
	}

	/**
	 * 取得某个字段的值
	 * @param obj 对象
	 * @param fieldName 字段名
	 * @return 字段的值
	 */
	public static Object getFieldValue(Object obj, String fieldName) throws IllegalAccessException {
		Field field = getField(obj.getClass(), fieldName);
		if(field == null) throw new IllegalAccessException("'" + fieldName + "' 字段不存在！");
		field.setAccessible(true);
		return field.get(obj);
	}

	/**
	 * 设置某个字段的值
	 * @param obj 对象
	 * @param fieldName 字段名
	 * @param value 字段的值
	 */
	public static void setFieldValue(Object obj, String fieldName, Object value) throws IllegalAccessException {
		Field field = getField(obj.getClass(), fieldName);
		if(field == null) throw new IllegalAccessException("'" + fieldName + "' 字段不存在！");
		field.setAccessible(true);
		field.set(obj, value);
	}

	/**
	 * 取得某个属性的值(通过getter函数)
	 * @param obj 对象
	 * @param name 属性名
	 * @return 属性的值
	 */
	public static Object getPorperty(Object obj, String name) throws InvocationTargetException, IllegalAccessException {
		// 获取 getter
		StringBuilder sb = new StringBuilder();
		sb.append("get").append(name.substring(0, 1).toUpperCase()).append(name.substring(1));
		Method getter = getMethod(obj.getClass(), sb.toString());
		// 如果没有 getter 则直接返回
		if(getter == null) { return null; }
		// 通过 getter 获取 Porperty
		return getter.invoke(obj);
	}

	/**
	 * 设置某个属性的值(通过setter函数)
	 * @param obj 对象
	 * @param name 属性名
	 * @param value 属性的值
	 */
	public static void setPorperty(Object obj, String name, Object value) throws InvocationTargetException, IllegalAccessException {
		// 获取 getter
		StringBuilder sb = new StringBuilder();
		sb.append("set").append(name.substring(0, 1).toUpperCase()).append(name.substring(1));
		Method setter = getMethod(obj.getClass(), sb.toString());
		// 如果没有 setter 则直接返回
		if(setter == null) { return; }
		// 通过 setter 设置 Porperty
		setter.invoke(obj, value);
	}

	/**
	 * 取得某个属性(通过getter函数)或字段的值
	 * @param obj 对象
	 * @param name 属性或字段名
	 * @return 值
	 */
	public static Object getPorpertyOrField(Object obj, String name) throws InvocationTargetException, IllegalAccessException {
		// 先尝试获取 getter
		StringBuilder sb = new StringBuilder();
		sb.append("get").append(name.substring(0, 1).toUpperCase()).append(name.substring(1));
		Method getter = getMethod(obj.getClass(), sb.toString());
		// 如果没有 getter 则直接获取 Field
		if(getter == null) { return getFieldValue(obj, name); }
		// 通过 getter 获取 Porperty
		return getter.invoke(obj);
	}

	/**
	 * 设置某个属性(通过setter函数)或字段的值
	 * @param obj 对象
	 * @param name 属性名
	 * @param value 属性的值
	 */
	public static void setPorpertyOrField(Object obj, String name, Object value) throws InvocationTargetException, IllegalAccessException {
		// 获取 getter
		StringBuilder sb = new StringBuilder();
		sb.append("set").append(name.substring(0, 1).toUpperCase()).append(name.substring(1));
		Method setter = getMethod(obj.getClass(), sb.toString());
		// 如果没有 setter 则直接设置 Field
		if(setter == null) { setFieldValue(obj, name, value); return; }
		// 通过 setter 设置 Porperty
		setter.invoke(obj, value);
	}

	/**
	 * 查找类的继承树上的指定名称的字段
	 */
	public static Field getField(Class<?> clazz, String fieldName) {
		// 如果到了 Object 类，就不再查找了
		if(clazz.equals(Object.class)) { return null; }

		Field field;
		try {
			field = clazz.getDeclaredField(fieldName);
			return field;
		} catch (NoSuchFieldException e) {
			// 本类中没有，尝试到父类中去查找
			return getField(clazz.getSuperclass(), fieldName);
		} catch (SecurityException e) {
			LOG.error("", e);
		}
		return null;
	}

	/**
	 * 查找类的继承树上的所有字段
	 */
	public static List<Field> getFields(Class<?> clazz) {
		if(clazz == null) { return null; }
		if(clazz.equals(Object.class)) { return null; } 	// 如果到了 Object 类，就不再查找了
		List<Field> all = new LinkedList<>();

		try {
			// 取本类的
			List<Field> list = Arrays.asList(clazz.getDeclaredFields());
			all.addAll(list);

			// 取父类的
			list = getFields(clazz.getSuperclass());
			if(list != null) {
				all.addAll(list);
			}

			return all;
		} catch (Exception e) {
			LOG.error("", e);
		}
		return null;
	}

	/**
	 * 查找类的继承树上的所有方法
	 */
	public static List<Method> getMethods(Class<?> clazz) {
		if(clazz == null) { return null; }
		if(clazz.equals(Object.class)) { return null; } 	// 如果到了 Object 类，就不再查找了
		List<Method> all = new ArrayList<>();

		try {
			// 取本类的
			List<Method> list = Arrays.asList(clazz.getDeclaredMethods());
			all.addAll(list);

			// 取父类的
			list = getMethods(clazz.getSuperclass());
			if(list != null) {
				all.addAll(list);
			}

			return all;
		} catch (Exception e) {
			LOG.error("", e);
		}
		return null;
	}

	/**
	 * 查找类的继承树上的指定类型的所有方法
	 * @param clazz 要查找的类
	 * @param staticOnly 只要静态函数（true）；只要非静态函数（false）；所有函数（null）
	 * @param methodName 要查找的方法名
	 * @param returnType 返回值类型。如果为 null 则不检查返回值。
	 * @param paramTypes 参数类型。
	 *   <li>如果没有参数，此处什么也不填（此情况下编译器会传入一个空数组），如：{@code getMethods(clazz, null, null, int.class)}
	 *   <li>如果填写参数为 null 则表示不检查参数（此情况下编译器会传入一个数组，第一个值是 null），如：{@code getMethods(clazz, null, null, int.class, null)}
	 * @return 所有符合要求的方法
	 */
	public static List<Method> getMethods(Class<?> clazz, Boolean staticOnly, String methodName, Class<?> returnType, Class<?>... paramTypes) {
		List<Method> ret = new ArrayList<>();
		// 如果传入了一个非空数组，且第一个值是 null，则表示不检查参数
		if(paramTypes != null && paramTypes.length > 0 && paramTypes[0] == null) { paramTypes = null; }

		try {
			List<Method> all = getMethods(clazz);
			for(Method method: all) {
				// 是否只查找静态方法
				if(staticOnly != null) {
					boolean isStatic = Modifier.isStatic(method.getModifiers());
					if(isStatic != staticOnly) { continue; }
				}
				// 检查方法名是否匹配
				if(methodName != null && !methodName.equals(method.getName())) { continue; }
				// 检查返回值类型是否匹配
				if(returnType != null && !Utils.isAssignableFrom(method.getReturnType(), returnType)) { continue; }
				// 检查的参数类型是否匹配
				if(paramTypes != null) {
					// 检查参数个数是否匹配
	                //if(method.getParameterCount() != paramTypes.length) { continue; } // 注意：此行代码在安卓8.0(API26)以下不支持！！！
					Class<?>[] requiredParamTypes = method.getParameterTypes();
	                if(requiredParamTypes.length != paramTypes.length) { continue; }

					// 检查参数类型是否匹配
					boolean paramTypesMatch = true;
					for(int i=0; i<paramTypes.length; i++) {
						// 判断所需要的参数类型是否可能使用给定的参数类型赋值
						if(!Utils.isAssignableFrom(requiredParamTypes[i], paramTypes[i])) {
							paramTypesMatch = false; break;
						}
					}

					// 如果参数类型匹配，则表示完全匹配，加入返回列表
					if(paramTypesMatch) { ret.add(method); }
				} else {
					// 不需要检查参数，表示匹配
					ret.add(method);
				}
			}
		} catch (Exception e) {
			LOG.error("", e);
		}

		return ret;
	}

	/**
	 * 查找类的继承树上的指定名称的方法
	 * <p>与 class.getMethod() 标准函数相比，扩展了参数类型的匹配，只要参数能相互转化，就认为是匹配的
	 * @param clazz 类
	 * @param methodName 方法名
	 * @param paramTypes 参数类型列表。如果无需参数，则传入 null
	 * @return 如果没有，返回 null；如果有多个，则抛出异常
	 */
	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
		// 如果没有参数，则传入空数组
		if(paramTypes == null) { paramTypes = new Class<?>[0]; }
		// 查找函数
		List<Method> methods = getMethods(clazz, null, methodName, null, paramTypes);
		if(methods.size() >= 1) { return methods.get(0); } // 取第一个？
		return null;
		//throw new RuntimeException("有多个同名函数，请明确指定参数类型列表！");
	}

	/**
	 * 检查方法名是否有效
	 */
	static final Pattern methodNamePattern = Pattern.compile("^[a-zA-Z_]+[a-zA-Z0-9_]*$");
	public static boolean validateMethodName(String name) {
		return (name != null) && (Utils.match(methodNamePattern, name) != null);
	}

	/**
	 * 调用用者信息
	 */
	public static final class CallerInfo {
		Class<?> clazz;
		String clazzName;
		String methodName;
		String filename;
		int lineNumber;

		public Class<?> getClazz() { return clazz; }
		public String getClazzName() { return clazzName; }
		public String getMethodName() { return methodName; }
		public String getFilename() { return filename; }
		public int getLineNumber() { return lineNumber; }

		@Override
		public String toString() {
			return "CallerInfo [" + clazzName + "#" + methodName + "(...) @ " + filename + "#" + lineNumber + "]";
		}

		CallerInfo(StackTraceElement se) {
			clazzName = se.getClassName();
			methodName = se.getMethodName();
			filename = se.getFileName();
			lineNumber = se.getLineNumber();

			try {
				clazz = Class.forName(clazzName, false, Thread.currentThread().getContextClassLoader());
			} catch(Exception ignored) {
			}
		}
	}

    /**
     * 取得调用此函数的类和函数信息
     * @param level 调用栈层级；
     * <li>0: getCallerInfo() 本身
     * <li>1: getCallerInfo() 的调用者
     * <li>2: getCallerInfo() 的调用者的调用者
     */
	public static CallerInfo getCallerInfo(int level) {
        StackTraceElement[] cause = Thread.currentThread().getStackTrace();

        boolean foundThisMethod = false;
        int currentLevel = -1;
        for(StackTraceElement se : cause) {
            // Skip entries until we get to the entry for this class
            String className = se.getClassName();
            String methodName = se.getMethodName();

            if(!foundThisMethod && Reflector.class.getName().equals(className) && "getCallerInfo".equals(methodName)) {
                foundThisMethod = true;
            }
            if (foundThisMethod) {
            	currentLevel++;
            	if(currentLevel >= level) {
            		return new CallerInfo(se);
            	}
            }
        }

        return null;
	}

	// For test only
	public static void main(String[] args) throws Exception {
		//call(Reflector.class, "test", "[\"hello, %s %d %f\", \"zy\", 123, 4.56]");
		System.out.println(validateMethodName("_a"));
		System.out.println(validateMethodName("api();abc"));

		System.out.println(getCallerInfo(0)); // CallerInfo [com.szwistar.common.datastruct.Reflector#getCallerInfo(...) @ Reflector.java#419]
		System.out.println(getCallerInfo(1)); // CallerInfo [com.szwistar.common.datastruct.Reflector#main(...) @ Reflector.java#449]
		System.out.println(getCallerInfo(2)); // null
	}
}
